<?php
/**
 * Created by PhpStorm.
 * User: 牛赞赞
 * Date: 2021/1/9
 * Time: 16:22
 */

namespace App\Task;


use App\Game\Conf\MainCmd;
use App\Game\Conf\SubCmd;
use App\Game\Core\AIRobot;
use App\Game\Core\Packet;
use Hyperf\Task\Annotation\Task;
use Hyperf\Utils\Context;
use Psr\Container\ContainerInterface;
use Swoole\WebSocket\Server as WebSocketServer;

class OutCardTimerTask
{
    /**
     * @var ContainerInterface
     */
    private $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * 出牌倒计时
     * @param $room_no
     * @param $next_chair_id
     * @param $hand
     * @throws \Exception
     */
    public function outCardTimer($room_no,$next_chair_id,$hand):void {
        $game_conf = config('game');
        $user_conf = config('user');
        $serv = server();
        $redis = redis();
        $fds = $this->getFdsByRoomNo($room_no);
        // 计时循环标识
        $this->setIsLoop($game_conf,$room_no,$next_chair_id,$hand);

        // 检查是否是机器人或托管
        $is_robot = $this->isRobotOrTakeOver($room_no,$next_chair_id);
        if($is_robot['code'] == 1){
            // 机器人出牌
            $this->robotOutCard($is_robot['data'],$is_robot['account']);
        }else{
            sleep($user_conf['play_timer']);// 等待20s
            $room_loop_key = sprintf($game_conf['user_room_loop'],$room_no);
            $room_data_key = sprintf($game_conf['user_room_data'],$room_no);
            $isLoop = $redis->hGet($room_loop_key,(string)$hand);//查询循环状态
            $user_room_data = $redis->hGetAll($room_data_key);
            $data_lock_key = sprintf($user_conf['user_room_data_lock'],$room_no);
            if(!empty($isLoop) && ($isLoop == $next_chair_id)){
                // 清除hash
                $redis->hDel($room_loop_key,(string)$hand);
                do {
                    //nx代表当key不存在时设置 ex代表设置过期时间
                    $result = $redis->set($data_lock_key, $room_no, ['nx', 'ex' =>1]);
                    if ($result) {
                        $this->setUserStatus($serv,$next_chair_id,$is_robot['account'],$room_no,$user_room_data,$fds);// 设置托管状态
                        $redis->del($data_lock_key);
                        continue;
                    }
                }while(!$result);
                // 重新获取房间数据
                $new_room_data = $redis->hGetAll($room_data_key);
                if(!isset($new_room_data['is_game_over']) || (isset($new_room_data['is_game_over']) && ($new_room_data['is_game_over'] == 0))){
                    $user_card = $this->getCardsByChairId($new_room_data,$next_chair_id);
                    $this->assignPlayRound($new_room_data,$user_card);
                }
            }
        }
    }

    /**
     * @param $game_conf
     * @param $room_no
     * @param $chair_id
     * @param $hand
     */
    protected function setIsLoop($game_conf,$room_no,$chair_id,$hand){
        $room_loop_key = sprintf($game_conf['user_room_loop'],$room_no);
        redis()->hSet($room_loop_key,(string)$hand,$chair_id);
        redis()->expire($room_loop_key,86400);
    }

    /**
     * 自动下发出牌逻辑
     * @param $user_room_data
     * @param $user_card
     * @throws \Exception
     */
    protected function assignPlayRound($user_room_data,$user_card){
        $my_card = $user_card['my_card'];
        //轮次
        $round = isset($user_room_data['round']) ?  $user_room_data['round'] + 1 : 0;
        //本轮次上一次出牌人
        $last_chair_id = isset($user_room_data['last_chair_id']) ?  $user_room_data['last_chair_id'] : 0;
        if(($round == 0) || ($last_chair_id == $my_card['chair_id'])) {
            // 最小出牌
            $out_cards = [
                'status'=>1,
                'chair_id'=>$my_card['chair_id'],
                'card'=>[$my_card['card'][0]]
            ];
        }else{
            // 过牌
            $out_cards = [
                'status'=>0,
                'chair_id'=>$my_card['chair_id'],
                'card'=>[]
            ];
        }
        // 投递出牌
        $task = container()->get(PlayCardTask::class);
        $task->playCard($user_room_data,$out_cards,$user_card['account'],$user_card['fd']);
    }

    /**
     * 根据账户获取连接
     * @param $room_data
     * @param $chair_id
     * @return bool|mixed|string
     */
    protected function getCardsByChairId($room_data,$chair_id){
        $game_conf = config('game');
        $my_card   = [];
        if(isset($room_data['uinfo'])){
            $user_info = json_decode($room_data['uinfo'],true);
            foreach ($user_info as $k=>$v){
                $user_data = json_decode($room_data[$k],true);
                if(!empty($user_data)){
                    if($user_data['chair_id'] == $chair_id){
                        $my_card['my_card'] = $user_data;
                        $my_card['account'] = $k;
                        $bind_key = sprintf($game_conf['user_bind_key'],$k);
                        $my_card['fd'] = redis()->get($bind_key);
                        break;
                    }
                }
            }
        }
        return $my_card;
    }

    /**
     * 获取房间玩家fd
     * @param $room_no
     * @return array
     */
    protected function getFdsByRoomNo($room_no){
        $game_conf = config('game');
        $room_wait_key = sprintf($game_conf['user_room_wait'],$room_no);
        $room_users = json_decode(redis()->get($room_wait_key),true);
        $fds = [];
        foreach ($room_users as $k=>$v){
            $bind_key = sprintf($game_conf['user_bind_key'],$v);
            $fds[] = redis()->get($bind_key);
        }
        return $fds;
    }

    /**
     * 对多用发送信息
     * @param $serv
     * @param $users
     * @param $data
     */
    protected function pushToUsers($serv, $users, $data)
    {
        foreach($users as $fd) {
            //获取客户端信息
            $client_info = $serv->getClientInfo($fd);
            $client[$fd] = $client_info;
            if(isset($client_info['websocket_status']) && $client_info['websocket_status'] == 3) {
                $serv->push($fd, $data, WEBSOCKET_OPCODE_BINARY);
            }
        }
    }

    /**
     * 设置托管消息并推送
     * @param $serv
     * @param $chair_id
     * @param $account
     * @param $room_no
     * @param $user_room_data
     * @param $fds
     */
    protected function setUserStatus($serv,$chair_id,$account,$room_no,$user_room_data,$fds){
        $game_conf = config('game');
        $room_data_key    = sprintf($game_conf['user_room_data'],$room_no);
        $redis = redis();
        $user_data = json_decode($user_room_data[$account],true);
        $user_data['take_over'] = 1;
        $redis->hSet($room_data_key,(string)$account,json_encode($user_data));
        //$a = json_decode($redis->hGet($room_data_key,(string)$account),true);
        //var_dump('更新托管：',$a);
        //通知房间玩家托管状态
        $msg  = ['chair_id'=>$chair_id,'take_over'=>1];
        $data = Packet::packFormat('OK', 1, $msg);
        $data = Packet::packEncode($data, MainCmd::CMD_SYS, SubCmd::SUB_GAME_TAKE_OVER);
        $this->pushToUsers($serv,$fds,$data);
    }

    /**
     * @param $account
     * @return bool|mixed|string
     */
    protected function getFdByAccount($account){
        $game_conf = config('game');
        $bind_key = sprintf($game_conf['user_bind_key'],$account);
        if(redis()->exists($bind_key)){
            return redis()->get($bind_key);
        }
    }

    /**
     * 检查是否是机器人
     * @param $room_no
     * @param $chair_id
     * @return array
     */
    protected function isRobotOrTakeOver($room_no,$chair_id){
        $game_conf       = config('game');
        $room_data_key   = sprintf($game_conf['user_room_data'],$room_no);
        $room_chair_key  = sprintf($game_conf['user_room_chair'],$room_no);
        $room_data       = redis()->hGetAll($room_data_key);
        $room_user_chair = json_decode(redis()->get($room_chair_key),true);
        $account = array_search($chair_id,$room_user_chair);
        $user_data    = json_decode($room_data[$account],true);
        if(($user_data['is_robot'] == 1) || ($user_data['take_over'] == 1)){
            $msg = ['code'=>1,'data'=>$room_data,'account'=>$account];
        }else{
            $msg = ['code'=>0,'data'=>[],'account'=>$account];
        }
        return $msg;
    }

    /**
     * 机器人出牌
     * @param $room_data
     * @param $account
     * @throws \Exception
     */
    protected function robotOutCard($room_data,$account){
        $my_card = json_decode($room_data[$account],true);
        $fd = $this->getFdByAccount($account);
        $robot = new AIRobot();
        $cards_types = $robot->allCardType($my_card['card']);
        //轮次
        $round = isset($room_data['round']) ?  $room_data['round'] + 1 : 0;
        //本轮次上一次出牌人
        $last_chair_id = isset($room_data['last_chair_id']) ?  $room_data['last_chair_id'] : 0;
        if(($round == 0) || ($last_chair_id == $my_card['chair_id'])) {
            if(!empty($cards_types[6])){
                // 顺子
                $out_cards = $cards_types[6][0];
            }elseif(!empty($cards_types[3])){
                $out_cards = $cards_types[3][0];
                // 三张
                if(!empty($cards_types[1])){
                    $single = $cards_types[1][0];
                    array_push($out_cards,$single);
                }elseif(!empty($cards_types[2])){
                    $double = $cards_types[2][0];
                    $out_cards = array_merge($out_cards,$double);
                }
            }elseif(!empty($cards_types[2])){
                // 对子
                $out_cards = $cards_types[2][0];
            }elseif(!empty($cards_types[1])){
                // 单张
                $out_cards = [$cards_types[1][0]];
            }elseif(!empty($cards_types[13])){
                // 炸弹
                $out_cards = $cards_types[13][0];
            }elseif(!empty($cards_types[14])){
                // 火箭
                $out_cards = $cards_types[14];
            }else{
                $out_cards = [$my_card['card'][0]];
            }
        }else{
            $last_card_type = $room_data['last_card_type'];
            $last_card_arr = json_decode($room_data['last_card'],true);
            $last_card = $robot->sortCardByGrade($last_card_arr);
            if($last_card_type == 14){
                $res = ['code'=>0,'data'=>[]];
            }elseif($last_card_type == 13){
                $res = $robot->ableZhaDan($last_card,$cards_types[13]);
            }elseif($last_card_type == 6){
                $res = $robot->ableShunZi($last_card,$cards_types[6]);
            }elseif(in_array($last_card_type,[3,4,5])){
                $res = $robot->ableSanDai($last_card_type,$last_card,$cards_types);
            }elseif($last_card_type == 2){
                $res = $robot->ableDuiZi($last_card,$cards_types[2]);
            }elseif($last_card_type == 1){
                $res = $robot->ableDan($last_card,$cards_types[1]);
            }else{
                $res = ['code'=>0,'data'=>[]];
            }
            if($res['code'] ==1){
                $out_cards = $res['data'];
            }else{
                $out_cards = [];
            }
        }
        //var_dump('出牌：',$out_cards);
        $out_cards_arr = [
            'status'=>0,
            'chair_id'=>$my_card['chair_id'],
            'card'=>[]
        ];
        if(!empty($out_cards)){
            $out_cards_arr = [
                'status'=>1,
                'chair_id'=>$my_card['chair_id'],
                'card'=>$out_cards
            ];
        }
        sleep(3);
        // 投递出牌
        $task = container()->get(PlayCardTask::class);
        $task->playCard($room_data,$out_cards_arr,$account,$fd);
    }
}